﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

using mvcForum.Core;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Core.Specifications;

using mvcForum.Web.Controllers;
using mvcForum.Web.Extensions;

using ForumLight = mvcForum.Web.Areas.ForumAPI.ViewModels.ForumLight;
using ForumVM = mvcForum.Web.Areas.ForumAPI.ViewModels.Forum;
using TopicLight = mvcForum.Web.Areas.ForumAPI.ViewModels.TopicLight;
using mvcForum.Web.Events;
using mvcForum.Core.Interfaces.Services;

namespace mvcForum.Web.Areas.ForumAPI.Controllers {

	public class ForumController : BaseAPIController {
		private readonly ITopicRepository topicRepo;
		private readonly IMembershipService membershipService;

		public ForumController(ITopicRepository topicRepo, IMembershipService membershipService)
			: base() {

			this.topicRepo = topicRepo;
			this.membershipService = membershipService;
		}

		[HttpPost]
		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Create(ForumVM model, String mode) {
			if (ModelState.IsValid) {
				if (model.CategoryId > 0) {
					IRepository<Core.Forum> forumRepo = this.GetRepository<Core.Forum>();
					IRepository<Category> catRepo = this.GetRepository<Category>();

					Core.Forum parentForum = null;
					Core.Forum newForum;
					if (model.ParentForumId.HasValue) {
						parentForum = forumRepo.Read(model.ParentForumId.Value);
						Category category = parentForum.Category;
						newForum = new Core.Forum(category, model.Name, model.SortOrder, model.Description, parentForum);
					}
					else {
						Category category = catRepo.Read(model.CategoryId);
						newForum = new Core.Forum(category, model.Name, model.SortOrder, model.Description);
					}

					forumRepo.Create(newForum);
					this.Context.SaveChanges();

					if (parentForum != null) {
						IRepository<ForumAccess> faRepo = this.GetRepository<ForumAccess>();
						IEnumerable<ForumAccess> accesses = faRepo.ReadMany(new ForumAccessSpecifications.SpecificForum(parentForum));

						foreach (ForumAccess fa in accesses) {
							faRepo.Create(new ForumAccess(newForum, fa.Group, fa.AccessMask));
						}

						this.Context.SaveChanges();
					}

					return new HttpStatusCodeResult(200);
				}
				else {
					// TODO:
					ModelState.AddModelError("", "");
				}
			}
			return new HttpStatusCodeResult(500, ModelState.ErrorString());
		}

		public ActionResult Read(Int32 id, String mode) {
			Core.Forum forum = this.GetRepository<Core.Forum>().Read(id);
			// TODO: Handle this somehow! A solution admin or a board admin with access to the board can always read the forum!!!!
			if (!this.membershipService.GetRolesForAccount(this.membershipService.GetAccount(userProvider.ActiveUser.ProviderId).AccountName).Contains(Constants.Roles.SolutionAdmin) && !forum.HasAccess(AccessFlag.Read)) {
				return new HttpStatusCodeResult(403, "Access is denied");
			}
			ForumVM model = new ForumVM { Id = forum.Id, Name = forum.Name, Description = forum.Description, SortOrder = forum.SortOrder, CategoryId = forum.Category.Id, CategoryUrl = Url.Action("read", "category", new { mode = "json", area = "forumapi", id = forum.Category.Id }) };
			if (forum.ParentForum != null) {
				model.ParentForumId = forum.ParentForum.Id;
				model.ParentForumUrl = Url.Action("read", "forum", new { mode = "json", area = "forumapi", id = model.ParentForumId });
			}
			return this.CustomJson(model);
		}

		[HttpPost]
		public ActionResult Update(ForumVM model, String mode) {
			if (ModelState.IsValid) {
				if (model.Id > 0) {
					Core.Forum forum = this.GetRepository<Core.Forum>().Read(model.Id);
					// TODO: Handle this somehow! A solution admin or a board admin with access to the board can always read the forum!!!!
					if (!this.membershipService.GetRolesForAccount(this.membershipService.GetAccount(userProvider.ActiveUser.ProviderId).AccountName).Contains(Constants.Roles.SolutionAdmin) && !forum.HasAccess(AccessFlag.Read)) {
						return new HttpStatusCodeResult(403, "Access is denied");
					}

					forum.Name = model.Name;
					forum.SortOrder = model.SortOrder;
					forum.Description = model.Description;

					this.Context.SaveChanges();

					return new HttpStatusCodeResult(200);
				}
				else {
					// TODO:
					ModelState.AddModelError("", "");
				}
			}
			return new HttpStatusCodeResult(500, ModelState.ErrorString());
		}

		[Authorize(Roles = Constants.Roles.Combined)]
		public ActionResult Delete(Int32 id, String mode, Boolean deleteAll) {
			IRepository<Core.Forum> forumRepo = this.GetRepository<Core.Forum>();

			Core.Forum forum = forumRepo.Read(id);

			Core.Forum parentForum = forum.ParentForum;

			if (parentForum != null) {
				// Let's clear all references from forum to posts/topics! Easier deleting topics/posts in a bit!
				forum.LastPostId = null;
				forum.LastPostUserId = null;
				forum.LastPostUsername = null;
				forum.LastPosted = null;
				forum.LastTopicId = null;

				this.context.SaveChanges();
			}

			DeleteForum(forum, forumRepo, deleteAll);

			if (parentForum != null) {
				NewAndUpdatedContentEventListener.UpdateForum(parentForum, this.context);
			}

			return new HttpStatusCodeResult(200);
		}

		private void DeleteForum(Core.Forum forum, IRepository<Core.Forum> forumRepo, Boolean deleteAll) {
			if (deleteAll) {
				// Let's clear all references from forum to posts/topics! Easier deleting topics/posts in a bit!
				forum.LastPostId = null;
				forum.LastPostUserId = null;
				forum.LastPostUsername = null;
				forum.LastPosted = null;
				forum.LastTopicId = null;

				this.context.SaveChanges();
			}

			if (forum.SubForums.Count > 0) {
				List<Core.Forum> forums = new List<Core.Forum>(forum.SubForums);
				foreach (Core.Forum child in forums) {
					DeleteForum(child, forumRepo, deleteAll);
				}
			}

			if (deleteAll) {
				#region Delete all element with references to the forum
				IRepository<FollowForum> ffRepo = this.GetRepository<FollowForum>();
				List<FollowForum> ffs = ffRepo.ReadMany(new FollowForumSpecifications.SpecificForum(forum)).ToList();
				foreach (FollowForum ff in ffs) {
					ffRepo.Delete(ff.Id);
				}

				IRepository<ForumTrack> ftRepo = this.GetRepository<ForumTrack>();
				List<ForumTrack> fts = ftRepo.ReadMany(new ForumTrackSpecifications.SpecificForum(forum)).ToList();
				foreach (ForumTrack ft in fts) {
					ftRepo.Delete(ft.Id);
				}

				IRepository<ForumAccess> faRepo = this.GetRepository<ForumAccess>();
				List<ForumAccess> fas = faRepo.ReadMany(new ForumAccessSpecifications.SpecificForum(forum)).ToList();
				foreach (ForumAccess fa in fas) {
					faRepo.Delete(fa.Id);
				}

				this.Context.SaveChanges();
				#endregion

				#region Delete topics and posts
				IRepository<Topic> topicRepo = this.GetRepository<Topic>();
				List<Topic> topics = new List<Topic>(forum.Topics);
				foreach (Topic t in topics) {
					t.LastPostId = null;
					t.LastPostUsername = null;
					t.LastPostAuthorId = null;

					IRepository<FollowTopic> fotRepo = this.GetRepository<FollowTopic>();
					List<FollowTopic> fots = fotRepo.ReadMany(new FollowTopicSpecifications.SpecificTopic(t)).ToList();
					foreach (FollowTopic fot in fots) {
						fotRepo.Delete(fot.Id);
					}

					IRepository<TopicTrack> ttRepo = this.GetRepository<TopicTrack>();
					List<TopicTrack> tts = ttRepo.ReadMany(new TopicTrackSpecifications.SpecificTopic(t)).ToList();
					foreach (TopicTrack tt in tts) {
						ttRepo.Delete(tt.Id);
					}

					this.context.SaveChanges();

					#region Delete posts
					IRepository<Post> postRepo = this.GetRepository<Post>();
					IEnumerable<Post> posts = new List<Post>(t.Posts).OrderByDescending(p => p.Posted);
					IRepository<PostReport> prRepo = this.GetRepository<PostReport>();
					foreach (Post p in posts) {
						List<PostReport> reports = prRepo.ReadMany(new PostReportSpecifications.SpecificPost(p)).ToList();
						foreach (PostReport pr in reports) {
							prRepo.Delete(pr.Id);
						}

						this.context.SaveChanges();

						postRepo.Delete(p);

						// TODO: !?!?!?!?
						this.context.SaveChanges();
					}
					#endregion

					topicRepo.Delete(t.Id);
				}

				this.context.SaveChanges();
				#endregion
			}

			forumRepo.Delete(forum);

			this.Context.SaveChanges();
		}

		public ActionResult List(Int32 id, String mode) {
			Core.Forum forum = this.GetRepository<Core.Forum>().Read(id);
			// TODO: Handle this somehow! A solution admin or a board admin with access to the board can always read the forum!!!!
			if (!this.membershipService.GetRolesForAccount(this.membershipService.GetAccountByName(userProvider.ActiveUser.Name).AccountName).Contains(Constants.Roles.SolutionAdmin) && !forum.HasAccess(AccessFlag.Read)) {
				return new HttpStatusCodeResult(403, "Access is denied");
			}

			return this.CustomJson(new {
				Forums = forum.SubForums.Select(f => new ForumLight {
					Id = f.Id,
					Name = f.Name,
					SortOrder = f.SortOrder,
					ApiUrl = Url.Action("read", "forum", new { mode = "json", area = "forumapi", id = f.Id })
				}).OrderBy(f => f.SortOrder)
			});
		}

		public ActionResult ListLatestTopics(String mode, Int32 id, Int32? count) {
			Int32 topicCount = 5;
			if (count.HasValue) {
				topicCount = count.Value;
			}
			IEnumerable<Topic> topics = new List<Topic>();

			Core.Forum forum = base.GetRepository<Core.Forum>().Read(id);
			if (forum != null && forum.HasAccess(AccessFlag.Read)) {
				// TODO: Performance ???
				topics = base.GetRepository<Topic>()
								.ReadAll()
								.Where(t => t.ForumId == forum.Id).AsQueryable()
								.Where(new TopicSpecifications.Visible().IsSatisfied)
								.OrderByDescending(t => t.Posted)
								.Take(topicCount);
				//topics = base.GetRepository<Topic>().ReadMany(new TopicSpecifications.LatestByForum(forum)).OrderByDescending(t => t.Posted).Take(topicCount);

				return this.CustomJson(new { Topics = topics.Select(t => new TopicLight { Id = t.Id, Flag = t.Flag, Title = t.Title, Posted = t.Posted, Type = t.Type, Url = Url.RouteUrl("ShowTopic", new { Title = t.Title.ToSlug(), Id = t.Id }), ApiUrl = Url.Action("read", "topic", new { mode = "json", area = "forumapi", id = t.Id }), Author = t.AuthorName }) });
			}

			return new HttpStatusCodeResult(404, "Not Found");
		}
	}
}